/**
 * @数组对象深复制
 * @param {Object}
 * @returns {Object}
 */
export function deepClone(source) {
    if (!source && typeof source !== 'object') {
        throw new Error('error arguments', 'deepClone')
    }
    const targetObj = source.constructor === Array ? [] : {}
    Object.keys(source).forEach(keys => {
        if (source[keys] && typeof source[keys] === 'object') {
            targetObj[keys] = deepClone(source[keys])
        } else {
            targetObj[keys] = source[keys]
        }
    })
    return targetObj
}

/**
 *  @比对两组数组过滤两者相同项
 *  @在策略模块种策略列表切换对比原始数据与新数据
 *  @param { old, now, parameter}
 *  @old初始数据add提交数据
 *
 */
export function filterSameData(old, now, parameter) {
    // 剔除相同 无变化的数据  保留两组不同的数据
    // old(原始数据) 为改变原始状态 now(最后一次修改) 为新增状态
    // for (let i = 0; i < now.length; i++) {
    //     for (let j = 0; j < old.length; j++) {
    //         if (now[i][parameter] == old[j][parameter]) {
    //             old.splice(j, 1)
    //             now.splice(i, 1)
    //             i--
    //             j--
    //             break
    //         }
    //     }
    //
    // }
    // return { remove: old, add: now }

    // 获取新增集合
    let add = deepClone(now)
    let remove = deepClone(old)

    if (!now.length || !old.length) return { remove, add }

    // 现 []
    // 源 [1,2]
    now.forEach(n => {
        remove = remove.filter(o => o[parameter] !== n[parameter])
    })
    old.forEach(o => {
        add = add.filter(n => n[parameter] !== o[parameter])
    })
    return { remove, add }
}

/**
 *
 *
 *  @下载文件流
 *
 */

export function download(data, filename) {
    if (!data) {
        return
    }
    const content = data
    const blob = new Blob([content])
    const fileName = filename
    if ('download' in document.createElement('a')) {
        // 非IE下载
        const elink = document.createElement('a')
        elink.download = fileName
        elink.style.display = 'none'
        elink.href = URL.createObjectURL(blob)
        document.body.appendChild(elink)
        elink.click()
        URL.revokeObjectURL(elink.href) // 释放URL 对象
        document.body.removeChild(elink)
    } else {
        // IE10+下载
        navigator.msSaveBlob(blob, fileName)
    }
}

/**
 *  @将平行数据转化为树型结构
 *  @param {data,id,pid,children}
 *  @returns { Array }
 *  重点：利用对象引用的特性
 */

export function makeTree(data = [], id = 'id', pid = 'pid', children = 'children') {

    let result = []
    if (!Array.isArray(data)) {
        console.log('非数组')
        return result
    }
    data.forEach(item => {
        delete item[children]
    })
    let map = {}
    /**
     *  1 将所有数据形成以id为key的对象map
     *  2 再遍历原数据,判断每一项的pid 是否指向map的键(其为每一项的id)
     *    2.1 不存在说明map中的id的值为顶级菜单
     *    2.1 存在 则将其当前数据放入与其对应的id数据的children项
     *        ### 由于对象属性为引用关系,所以遍历一维数组 判断每一项的pid 与 每一项的id 是否存在指向,
     *            存在指向都会形成一个children属性
     *            遍历完成后,即完成对每一项实现关系关联,他们自动形成嵌套关系
     */
    data.forEach(item => {
        map[item[id]] = item
    })
    data.forEach(item => {
        let parent = map[item[pid]]
        /**
         *  除顶级菜单下的所有符合pid属性的分布设置或者放入children属性中
         *  item 都受其影响
         *
         */
        if (parent) {
            (parent[children] || (parent[children] = [])).push(item)

        } else {
            /**
             *  属于顶级菜单的item放入result中
             *  其余item的修改都会指向result中的item
             */
            result.push(item)
        }

    })
    return result
}

/**
 *  @正则表达式创造replaceAll方法
 *  @使用newRegExp构造一个新的表达式
 *  @全局替换
 *  @param { oldV, newV, str}
 *  @returns { String }
 */

export function replaceString(oldV, newV, str) {
    let reg = new RegExp(oldV, 'g')
    return str.replace(reg, newV)
}

/**
 *  @字节转换方法
 */

export function bytesToSize(bytes) {
    if (bytes === 0) return '0 B'

    let k = 1024

    let sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']

    i = Math.floor(Math.log(bytes) / Math.log(k))

    return (bytes / Math.pow(k, i)) + ' ' + sizes[i]                                                                                                                 //return (bytes / Math.pow(k, i)).toPrecision(3) + ' ' + sizes[i];
}

/**
 *  @计算时间差  1:10:11
 *  @param  开始时间 结束时间
 */

export function timeDifference(startTime, endTime) {
    let computeTime = parseInt(endTime) - parseInt(startTime)   //时间差的毫秒数
    //计算出小时数
    let leave1 = computeTime % (24 * 3600 * 1000)    //计算天数后剩余的毫秒数
    let hours = Math.floor(leave1 / (3600 * 1000))
    //计算相差分钟数
    let leave2 = leave1 % (3600 * 1000)        //计算小时数后剩余的毫秒数
    let minutes = Math.floor(leave2 / (60 * 1000))
    //计算相差秒数
    let leave3 = leave2 % (60 * 1000)      //计算分钟数后剩余的毫秒数
    let seconds = Math.round(leave3 / 1000)
    // return  hours + "小时 " + minutes + " 分钟" + seconds + " 秒"
    hours = hours > 9 ? hours : '0' + hours
    minutes = minutes > 9 ? minutes : '0' + minutes
    seconds = seconds > 9 ? seconds : '0' + seconds
    return `${ hours }:${ minutes }:${ seconds }`
}

/**
 *  @将总时长转换
 *  @param 单位（毫秒）
 *  @return 该毫秒数转换为 * days * hours * minutes * seconds 后的格式
 *
 */
export function formatTimeDuring(mss = 0) {
    let days = Math.floor(mss / (1000 * 60 * 60 * 24))
    let hours = Math.floor((mss % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60))
    let minutes = Math.floor((mss % (1000 * 60 * 60)) / (1000 * 60))
    let seconds = Math.round((mss % (1000 * 60)) / 1000)
    return days + '天' + hours + '小时' + minutes + '分钟' + (seconds ? seconds + '秒' : '')
}

/**
 *  @创建随机key
 *  @param 随机数
 *  @returns String
 *
 */
export function createRandomId() {
    return (Math.random() * 10000000).toString(16).substr(0, 4) + '-' + (new Date()).getTime() + '-' + Math.random().toString().substr(2, 5)
}

/**
 *  @创建时间选项 月 时 分
 */
export class CreateTimeOption {
    constructor(step, len, unit) {
        this.step = step
        this.len = len
        this.unit = unit
        // 时、分 初始值为0 日期初始值为 1
        this.initIndex = ['时', '分'].indexOf(unit) > -1 ? 0 : 1
    }

    getOption() {
        let arr = []
        let _label = ''
        for (let i = this.initIndex; i < this.len; i++) {
            if (!(i % this.step)) {
                _label = i.toString()
                if (i < 10) {
                    _label = '0' + _label
                }
                arr.push({
                    value: i,
                    label: _label + this.unit
                })
            }
        }
        return arr
    }
}

/**
 * @desc 函数节流
 * @param func 函数
 * @param wait 延迟执行毫秒数
 * @param type 1 表时间戳版，2 表定时器版
 */
export function throttle(func, wait, type) {
    if (type === 1) {
        var previous = 0
    } else if (type === 2) {
        var timeout
    }
    return function () {
        let context = this
        let args = arguments
        if (type === 1) {
            let now = Date.now()

            if (now - previous > wait) {
                func.apply(context, args)
                previous = now
            }
        } else if (type === 2) {
            if (!timeout) {
                timeout = setTimeout(() => {
                    timeout = null
                    func.apply(context, args)
                }, wait)
            }
        }
    }
}

/**
 * @desc 函数防抖 （预防重复点击）
 * @param func 函数
 * @param wait 延迟执行毫秒数
 * @param immediate true 表立即执行，false 表非立即执行
 */
export function debounce(func, wait, immediate) {
    let timeout

    return function () {
        let context = this
        let args = arguments

        if (timeout) clearTimeout(timeout)
        if (immediate) {
            var callNow = !timeout
            timeout = setTimeout(() => {
                timeout = null
            }, wait)
            if (callNow) func.apply(context, args)
        } else {
            timeout = setTimeout(function () {
                func.apply(context, args)
            }, wait)
        }
    }
}
